#include "windows.h"
#include "stdio.h"

#define MAX_BUFFER_SIZE 8192
typedef struct __OverlappedData {
    OVERLAPPED overlapped;
    char buffer[MAX_BUFFER_SIZE];
    DWORD bytesReturned;
    HANDLE directoryHandle;
}OverlappedData;
void WatchDirectoryChanges(const wchar_t* directory, long long size, void (*save_eunum_file)(wchar_t* file,int action, void*), void* obj) {
    HANDLE directoryHandle = CreateFileW(directory, FILE_LIST_DIRECTORY, 
                                  FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 
                                  NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, NULL);

    if (directoryHandle == INVALID_HANDLE_VALUE) {
        wprintf(L"Failed to open directory. Error: %d\n", GetLastError());
        return;
    }
    HANDLE completionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
    if (CreateIoCompletionPort(directoryHandle, completionPort, (ULONG_PTR)1, 0) == NULL) {
        wprintf(L"Failed to associate directory handle with completion port. Error: %d\n", GetLastError());
        CloseHandle(directoryHandle);
        return;
    }
    OverlappedData* data = (OverlappedData*)malloc(sizeof(OverlappedData));
    ZeroMemory(&data->overlapped, sizeof(data->overlapped));
    data->overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
    data->directoryHandle = directoryHandle;
    BOOL result = ReadDirectoryChangesW(
        directoryHandle,
        data->buffer,
        MAX_BUFFER_SIZE,
        TRUE,
        FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME | FILE_NOTIFY_CHANGE_SIZE |
        FILE_NOTIFY_CHANGE_LAST_WRITE,
        &data->bytesReturned,
        &data->overlapped,
        NULL
    );
    if (!result) {
        wprintf(L"Error in ReadDirectoryChangesW: %d\n", GetLastError());
        free(data);
        CloseHandle(directoryHandle);
        CloseHandle(completionPort);
        return;
    }
    DWORD bytesTransferred;
    ULONG_PTR completionKey;
    LPOVERLAPPED overlapped;
    while (TRUE) {
        if (!GetQueuedCompletionStatus(completionPort, &bytesTransferred, &completionKey, &overlapped, INFINITE)) {
            wprintf(L"Error in GetQueuedCompletionStatus: %d\n", GetLastError());
            break;
        }
        OverlappedData* data = (OverlappedData*)overlapped;
        if (data->bytesReturned > 0) {
            FILE_NOTIFY_INFORMATION* info = (FILE_NOTIFY_INFORMATION*)data->buffer;
            do {
                info->FileName[info->FileNameLength / sizeof(wchar_t)]=0;
                save_eunum_file(info->FileName, info->Action, obj);
                if (info->NextEntryOffset == 0)
                    break;
                info = (FILE_NOTIFY_INFORMATION*)((char*)(info) + info->NextEntryOffset);
            } while (TRUE);
        }
        ZeroMemory(&data->overlapped, sizeof(data->overlapped));
        data->overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
        result = ReadDirectoryChangesW(
            data->directoryHandle,
            data->buffer,
            MAX_BUFFER_SIZE,
            TRUE,
            FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME | FILE_NOTIFY_CHANGE_SIZE |
            FILE_NOTIFY_CHANGE_LAST_WRITE,
            &data->bytesReturned,
            &data->overlapped,
            NULL
        );
        if (!result) {
            wprintf(L"Error in ReadDirectoryChangesW: %d\n", GetLastError());
            break;
        }
    }

    free(data);
    CloseHandle(directoryHandle);
    CloseHandle(completionPort);
}
void ScanDirectory(wchar_t* directory, long long size, BOOL recursive, void (*save_eunum_file)(wchar_t* path,wchar_t* file, int action, void*), void* obj)
{
    WIN32_FIND_DATAW findData;
    HANDLE hFind = FindFirstFileW(wcsncat(directory,L"\\*",2), &findData);
    directory[wcslen(directory)-1]=L'\0';
    if (hFind == INVALID_HANDLE_VALUE){
        //wprintf(L"Failed to open directory: %ls\n", directory);
        return;
    }
    do{
        if (findData.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY){
            if (wcscmp(findData.cFileName,L".")!=0&&wcscmp(findData.cFileName,L"..")!=0){
                save_eunum_file(directory, findData.cFileName, 1, obj);
                if(recursive) ScanDirectory(directory,size,recursive,save_eunum_file, obj);
            }
        }else{
            save_eunum_file(directory, findData.cFileName, 0, obj);
        }
    } while (FindNextFileW(hFind, &findData));
    FindClose(hFind);
}